package top.kaoshanji.p1ch03b;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import top.kaoshanji.u1constant.FileConstant;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 代码清单 p1ch03b-17：基于文件系统中静态文件的HTTP服务器
 * author kaoshanji
 * ^_^
 * create time 2025/1/12 17:48
 */
public class AsynchronousServerSocketChannelExample02 {

    private static final Logger logger = LogManager.getLogger(AsynchronousServerSocketChannelExample02.class);

    private static final Pattern PATH_EXTRACTOR = Pattern.compile("GET (.*?) HTTP");
    private static final String INDEX_PAGE = "index.html";

    public static void main(String[] args) throws IOException {
        logger.info("...代码清单 p1ch03b-17：基于文件系统中静态文件的HTTP服务器...");

        AsynchronousServerSocketChannelExample02 example = new AsynchronousServerSocketChannelExample02();
        example.start(Paths.get(FileConstant.LOCAL_ROOT_PATH.concat("p1ch03b17")).toAbsolutePath());

    }

    public void start(final Path root) throws IOException {

        AsynchronousChannelGroup group = AsynchronousChannelGroup.withFixedThreadPool(10, Executors.defaultThreadFactory());
        final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(10800));

        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {

            @Override
            public void completed(AsynchronousSocketChannel clientChannel, Void attachement) {
                serverChannel.accept(null, this);

                try {
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    clientChannel.read(buffer).get();
                    buffer.flip();

                    String request = new String(buffer.array());
                    String requestPath = extractPath(request);
                    Path filePath = getFilePath(root, requestPath);

                    if (!Files.exists(filePath)) {
                        String error404 = generateErrorResponse(404, "Not Found");
                        clientChannel.write(ByteBuffer.wrap(error404.getBytes()));
                        return;
                    }

                    logger.info("...处理请求：{}...", requestPath);
                    String header = generateFileContentResponseHeader(filePath);
                    clientChannel.write(ByteBuffer.wrap(header.getBytes())).get();

                    Files.copy(filePath, Channels.newOutputStream(clientChannel));

                } catch (ExecutionException | InterruptedException | IOException e) {
                    String error = generateErrorResponse(500, "Internal Server Error");
                    clientChannel.write(ByteBuffer.wrap(error.getBytes()));
                    logger.error(e.getMessage());
                } finally {
                    try {
                        clientChannel.close();
                    } catch (IOException e) {
                        logger.error(e.getMessage());
                    }

                }
            }

            @Override
            public void failed(Throwable throwable, Void attachment) {
                logger.error(throwable.getMessage());
            }
        });

        logger.info("...服务器已经启动，文件根目录为：{}...", root);
    }

    private String extractPath(String request) {
        Matcher matcher = PATH_EXTRACTOR.matcher(request);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    private Path getFilePath(Path root, String requestPath) {
        if (requestPath == null || "/".equals(requestPath)) {
            requestPath = INDEX_PAGE;
        }
        if (requestPath.startsWith("/")) {
            requestPath = requestPath.substring(1);
        }
        int pos = requestPath.indexOf("?");
        if (pos != -1) {
            requestPath = requestPath.substring(0, pos);
        }

        return root.resolve(requestPath);
    }

    private String generateErrorResponse(int statusCode, String message) {
        StringBuilder builder = new StringBuilder();
        builder.append("HTTP/1.1 ").append(statusCode).append(" ").append(message).append("\r\n");
        builder.append("Content-Type: text/plain\r\n");
        builder.append("Content-Length: ").append(message.length()).append("\r\n");
        builder.append("\r\n");
        builder.append(message);
        return builder.toString();
    }

    private String generateFileContentResponseHeader(Path filePath) throws IOException {
        StringBuilder builder = new StringBuilder();
        builder.append("HTTP/1.1 200 OK\r\n");
        builder.append("Content-Type: ");
        builder.append(getContentType(filePath));
        builder.append("\r\n");
        builder.append("Content-Length: ").append(Files.size(filePath)).append("\r\n");
        builder.append("\r\n");
        return builder.toString();
    }

    private String getContentType(Path filePath) throws IOException {
        return Files.probeContentType(filePath);
    }


}
